package com.gitee.qdbp.jdbc.plugins.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.gitee.qdbp.able.jdbc.model.PkEntity;
import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations;
import com.gitee.qdbp.jdbc.api.SqlOperator;
import com.gitee.qdbp.jdbc.model.OmitStrategy;
import com.gitee.qdbp.jdbc.model.ValidStrategy;
import com.gitee.qdbp.jdbc.plugins.BatchInsertExecutor;
import com.gitee.qdbp.jdbc.sql.SqlBuffer;
import com.gitee.qdbp.jdbc.sql.SqlBuilder;
import com.gitee.qdbp.jdbc.sql.build.CrudSqlHelper;
import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper;

/**
 * UNION ALL FROM DUAL批量新增接口实现类(要求字段对齐)<pre>
    INSERT INTO {tableName}(FIELD1, FIELD2, FIELD3)
        SELECT field11, field12, ..., field1n FROM DUAL
        UNION ALL
        SELECT field21, field22, ..., field2n FROM DUAL
        UNION ALL
        SELECT fieldn1, fieldn2, ..., fieldnn FROM DUAL</pre>
 * 
 * @author zhaohuihua
 * @version 20200707
 */
public class BatchInsertByUnionAllFromDualExecutor extends BatchBaseExecutor implements BatchInsertExecutor {

    // Oracle的CLOB字段批量新增时, 如果CLOB字段值既有小于4000的也有大于4000的
    // 就会报错ORA-01790: 表达式必须具有与对应表达式相同的数据类型
    // 原因是JdbcTemplate对CLOB的处理, 小于4000的用ps.setString(), 超过4000的用ps.setClob()
    // 详见StatementCreatorUtils.setValue()

    public BatchInsertByUnionAllFromDualExecutor() {
        super("oracle");
    }

    @Override
    public List<String> inserts(List<PkEntity> entities, ValidStrategy validStrategy, SqlBufferJdbcOperations jdbc, SqlOperator<CrudSqlHelper> sqlOperator) {
        CrudFragmentHelper sqlHelper = sqlOperator.sqlHelper().fragment();
        String tableName = sqlHelper.getTableName();
        Set<String> fieldNames = mergeFields(entities);
        SqlBuffer fieldsSqlBuffer = sqlHelper.buildInsertFieldsSql(fieldNames);

        // 获取批量操作语句的省略策略配置项
        OmitStrategy omits = sqlOperator.plugins().getOmitStrategyOfBatchExecutor();

        // INSERT INTO {tableName}(FIELD1, FIELD2, FIELD3) 
        //     SELECT field11, field12, ..., field1n FROM DUAL
        //     UNION ALL
        //     SELECT field21, field22, ..., field2n FROM DUAL
        //     UNION ALL
        //     SELECT fieldn1, fieldn2, ..., fieldnn FROM DUAL
        SqlBuilder sql = sqlOperator.newSqlBuilder();
        // INSERT INTO (...)
        sql.ad("INSERT INTO").ad(tableName);
        sql.ad('(').ad(fieldsSqlBuffer).ad(')').newline().tab();
        List<String> ids = new ArrayList<>();
        int size = entities.size();
        for (int i = 0; i < size; i++) {
            PkEntity item = entities.get(i);
            String id = item.getPrimaryKey();
            ids.add(id);
            Map<String, Object> entity = item.getEntity();
            SqlBuffer valuesSqlBuffer = sqlHelper.buildInsertValuesSql(fieldNames, entity, validStrategy);
            if (i > 0) {
                sql.newline().ad("UNION ALL").newline();
            }
            if (omits.getMinSize() > 0 && size > omits.getMinSize()) {
                sql.omit(i, size, omits.getKeepSize()); // 插入省略标记
            }
            sql.ad("SELECT").ad(valuesSqlBuffer).ad("FROM DUAL");
        }

        // 执行批量数据库插入
        jdbc.batchInsert(sql.out());
        return ids;
    }
}
